#include <libwebsockets.h>
#include <signal.h>

static int interrupted;
void sigint_handler(int sig)
{
	interrupted = 1;
}

	#define MAX_PAYLOAD_SIZE  (10 * 1024)
	static struct lws_context_creation_info ctx_info = { 0 };
	static struct lws_context *context = NULL;
/**
 * 会话上下文对象，结构根据需要自定义
 */
struct session_data {
    int msg_count;
    unsigned char buf[LWS_PRE + MAX_PAYLOAD_SIZE];
    size_t len;
    _Bool bin;
    _Bool fin;
};

int set_ssl(const char* ca_filepath, 
                            const char* server_cert_filepath,
                            const char*server_private_key_filepath,
                            _Bool is_support_ssl)
{
    if(!is_support_ssl)
    {
        ctx_info.ssl_ca_filepath = NULL;
        ctx_info.ssl_cert_filepath = NULL;
        ctx_info.ssl_private_key_filepath = NULL;
    }
    else
    {
        ctx_info.ssl_ca_filepath = ca_filepath;
        ctx_info.ssl_cert_filepath = server_cert_filepath;
        ctx_info.ssl_private_key_filepath = server_private_key_filepath;
        ctx_info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
    //ctx_info.options |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT;
    }

    return is_support_ssl;
}

/* 服务器底层实现的回调函数 */
static int protocol_my_callback(struct lws *wsi, 
                        enum lws_callback_reasons reason, 
                        void *user, 
                        void *in, 
                        size_t len )
{
    struct session_data *data = (struct session_data *) user;

    switch (reason) {
		case LWS_CALLBACK_PROTOCOL_INIT:
			break;
		case LWS_CALLBACK_PROTOCOL_DESTROY:
			break;
		case LWS_CALLBACK_WSI_CREATE:
			break;
		case LWS_CALLBACK_WSI_DESTROY:
			break;
        case LWS_CALLBACK_ESTABLISHED:
			// 当服务器和客户端完成握手后
            lwsl_notice("Client connect!\n");
            break;
        case LWS_CALLBACK_RECEIVE:
			// 当接收到客户端发来的帧以后
            // 判断是否最后一帧
            data->fin = lws_is_final_fragment( wsi );
            // 判断是否二进制消息
            data->bin = lws_frame_is_binary( wsi );
            // 对服务器的接收端进行流量控制，如果来不及处理，可以控制之
            // 下面的调用禁止在此连接上接收数据
            lws_rx_flow_control( wsi, 0 );
 
            // 业务处理部分，为了实现Echo服务器，把客户端数据保存起来
            memcpy( &data->buf[ LWS_PRE ], in, len );
            data->len = len;
            lwsl_notice("recvied message:%s\n",&data->buf[ LWS_PRE ]);
 
            // 需要给客户端应答时，触发一次写回调
            lws_callback_on_writable(wsi);
            data = NULL;
            break;

        case LWS_CALLBACK_SERVER_WRITEABLE:   // 当此连接可写时
            lws_write( wsi, &data->buf[ LWS_PRE ], data->len, LWS_WRITE_TEXT );
            // 下面的调用允许在此连接上接收数据
            lws_rx_flow_control( wsi, 1 );
            break;
		default:
			break;
    }

    // 回调函数最终要返回0，否则无法创建服务器
    return 0;
}

/**
 * 支持的WebSocket子协议数组
 * 子协议即JavaScript客户端WebSocket(url, protocols)第2参数数组的元素
 * 你需要为每种协议提供回调函数
 */
struct lws_protocols protocols[] = {
    {
        //协议名称，协议回调，接收缓冲区大小
        "ws", protocol_my_callback, sizeof( struct session_data ), MAX_PAYLOAD_SIZE, 0, NULL, 0
    },
    {
		// 最后一个元素固定为此格式
        NULL, NULL, 0, 0, 0, NULL, 0
    }
};

int main(int argc,char **argv)
{
    //接收SIGINT(ctrl+c)信号
    signal(SIGINT, sigint_handler);
    int n = 0;

	//初始化服务器
	ctx_info.port = 8000;   //可以指定端口，这里指定了8000
    ctx_info.iface = NULL; // 在所有网络接口上监听
    ctx_info.protocols = protocols;
    ctx_info.gid = (uid_t)-1;
    ctx_info.uid = (gid_t)-1;
    ctx_info.options = LWS_SERVER_OPTION_VALIDATE_UTF8;

    //设置ssl（不使用ssl则传空，使用则传入证书文件路径）
    set_ssl(NULL,NULL,NULL,0);

	////创建服务器
	context = lws_create_context(&ctx_info);
    if (!context) {
		lwsl_err("lws_server create failed\n");
		return -1;
	}

    //服务器运行（运行时可设置间隔等待时间，这里为1000，单位为ms）
    while(n >= 0 && !interrupted)
        n = lws_service(context, 1000);

    //销毁资源
    lws_context_destroy(context);

    return 0;
}
